package com.qsn.tool.generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import static com.qsn.tool.generator.GeneratorConstant.TEMPLATE_PATH;


/**
 * 代码生成器
 *
 * @author qiusn
 * @date 2019-10-16
 */
public class GoGeneratorMain {
    /**
     * 表名字，可用“,”隔开; 这里注意逗号后面不能有空格
     */
    public static final String tableName = GeneratorConstant.GENERATOR_TABLE_NAME;

    /**
     * 数据源
     */
    public static final String dbUrl = GeneratorConstant.DB_URL;
    public static final String dbUserName = GeneratorConstant.DB_USER_NAME;
    public static final String dbPassword = GeneratorConstant.DB_PASSWORD;

    /**
     * 作者名
     */
    public static final String authorName = GeneratorConstant.GENERATOR_AUTHOR_NAME;

    /**
     * 是否覆盖文件
     */
    public static final Boolean booFileOverride = GeneratorConstant.BOO_FILE_OVERRIDE;

    /**
     * 包配置路径
     */
    public static final String packagePath = GeneratorConstant.GENERATOR_PACKAGE_PATH;

    /**
     * 项目路径  当前项目(固定的：可获取当前项目绝对路径， 后可利用拼接 javaPath)
     */
    public static final String projectPath = System.getProperty("user.dir");

    /**
     * 输出路径
     */
    public static final String javaPath = "/src/main/java";


    /**
     * 自定义模板路径（没有特殊需求，不需要改）
     */
    public static final String entityTemplatePath = String.format("/templates/%s/entity.java", TEMPLATE_PATH);
    public static final String mapperTemplatePath = String.format("/templates/%s/mapper.java", TEMPLATE_PATH);
    public static final String mapperXmlTemplatePath = String.format("/templates/%s/mapper.xml.ftl", TEMPLATE_PATH);
    public static final String serviceTemplatePath = String.format("/templates/%s/service.java", TEMPLATE_PATH);
    public static final String serviceImplTemplatePath = String.format("/templates/%s/serviceImpl.java", TEMPLATE_PATH);
    public static final String controllerTemplatePath = String.format("/templates/%s/controller.java", TEMPLATE_PATH);
    public static final String poTemplatePath = String.format("/templates/%s/po.java.ftl", TEMPLATE_PATH);
    public static final String dtoTemplatePath = String.format("/templates/%s/dto.java.ftl", TEMPLATE_PATH);
    // 自定义的普通单个query与VO
    public static final String queryTemplatePath = String.format("/templates/%s/query.java.ftl", TEMPLATE_PATH);
    public static final String voTemplatePath = String.format("/templates/%s/vo.java.ftl", TEMPLATE_PATH);
    // 自定义复杂的query与VO
    public static final String queryPageTemplatePath = String.format("/templates/%s/queryPage.java.ftl", TEMPLATE_PATH);
    public static final String queryListTemplatePath = String.format("/templates/%s/queryList.java.ftl", TEMPLATE_PATH);
    public static final String queryInfoTemplatePath = String.format("/templates/%s/queryInfo.java.ftl", TEMPLATE_PATH);
    public static final String queryInsertTemplatePath = String.format("/templates/%s/queryInsert.java.ftl", TEMPLATE_PATH);
    public static final String queryUpdateTemplatePath = String.format("/templates/%s/queryUpdate.java.ftl", TEMPLATE_PATH);

    public static final String voPageTemplatePath = String.format("/templates/%s/voPage.java.ftl", TEMPLATE_PATH);
    public static final String voListTemplatePath = String.format("/templates/%s/voList.java.ftl", TEMPLATE_PATH);
    public static final String voInfoTemplatePath = String.format("/templates/%s/voInfo.java.ftl", TEMPLATE_PATH);


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println(String.format("---------------------您想生成表 %s 代码吗(此操作有可能覆盖已有代码)---------------------", tableName));
        System.out.println("请输入yes/no?");
        String result = scanner.next();
        if (!"yes".equals(result.toLowerCase())) {
            System.err.println("您没有同意生成代码");
            return;
        }

        AutoGenerator generator = new AutoGenerator();

        // 全局变量配置
        generator.setGlobalConfig(allConstant());

        // 数据源配置
        generator.setDataSource(setDataSource());

        // 包配置
        generator.setPackageInfo(setPackage());

        // 自定义配置
        generator.setCfg(setFileOutConfigList());

        // 配置模板
        generator.setTemplate(setTemplateConfig());

        // 表相关配置
        generator.setStrategy(setTable());

        // 模板引擎
        generator.setTemplateEngine(new FreemarkerTemplateEngine());
        generator.execute();
    }

    /**
     * 全局变量配置
     */
    private static GlobalConfig allConstant() {
        GlobalConfig gc = new GlobalConfig();
        // 输出路径
        gc.setOutputDir(projectPath + javaPath);
        // 是否覆盖已生成文件
        gc.setFileOverride(booFileOverride);
        // 默认true ,是否打开输出目录
        gc.setOpen(false);
        // 默认false,是否开启二级缓存
        gc.setEnableCache(false);
        // 作者
        gc.setAuthor(authorName);
        // 默认false
        gc.setSwagger2(false);
        // xml 的resultMap
        gc.setBaseResultMap(true);
        // 时间策略 默认TIME_PACK
        gc.setDateType(DateType.TIME_PACK);
        //默认false  和 base model相似
        gc.setBaseColumnList(true);
        // 自定义文件命名，注意 %s 会自动填充表实体属性！
//        gc.setEntityName("%s");
        gc.setControllerName("%sController");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        // 指定生成的主键类型
//        gc.setIdType(IdType.ID_WORKER_STR);
        return gc;
    }

    /**
     * 数据源配置
     */
    private static DataSourceConfig setDataSource() {
        // 数据源配置
        DataSourceConfig dc = new DataSourceConfig();
        // 数据库信息查询 //默认mysql
        dc.setDbQuery(new MySqlQuery());
        // 数据库类型
        dc.setDbType(DbType.MYSQL);
        // 类型转换 默认mysql
        dc.setTypeConvert(new MySqlTypeConvert());
        dc.setUrl(dbUrl);
        dc.setDriverName("com.mysql.cj.jdbc.Driver");
        dc.setUsername(dbUserName);
        dc.setPassword(dbPassword);
        return dc;
    }

    /**
     * 包名及路径配置
     */
    private static PackageConfig setPackage() {
        PackageConfig pc = new PackageConfig();
        pc.setParent(packagePath);
//        pc.setModuleName(""); //此处是所属模块名称
//        pc.setEntity("entity"); //默认entity,controller,service,service.impl,mapper1,mapper1.xml
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
//        pc.setEntity("entity");
        pc.setMapper("mapper");
        return pc;
    }

    /**
     * 自定义配置
     */
    private static InjectionConfig setFileOutConfigList() {
        PackageConfig pc = new PackageConfig();
        pc.setParent(packagePath);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing

            }
        };
        /**
         * 将xml生成到resource下面、
         */
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出

        // 自定义mapper
        focList.add(new FileOutConfig(mapperXmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return projectPath + "/src/main/resources/mapper/"
                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                // getEntityName() xml的文件名是下滑线
                // getName() xml的文件名是大小写的命名
            }
        });
        if (TEMPLATE_PATH.equals("standard") || TEMPLATE_PATH.equals("lxdb")) {
            // 自定义PO
            focList.add(new FileOutConfig(poTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/po/"
                            + tableInfo.getEntityName() + "PO" + StringPool.DOT_JAVA;
                }
            });
            // 自定义DTO
            focList.add(new FileOutConfig(dtoTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/dto/"
                            + tableInfo.getEntityName() + "DTO" + StringPool.DOT_JAVA;
                }
            });
            cfg.setFileOutConfigList(focList);

//            // 自定义 普通单个QUERY
//            focList.add(new FileOutConfig(queryTemplatePath) {
//                @Override
//                public String outputFile(TableInfo tableInfo) {
//                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
//                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
//                            + tableInfo.getEntityName() + "Query" + StringPool.DOT_JAVA;
//                }
//            });
//            cfg.setFileOutConfigList(focList);

//            // 自定义 普通单个VO
//            focList.add(new FileOutConfig(voTemplatePath) {
//                @Override
//                public String outputFile(TableInfo tableInfo) {
//                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
//                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/vo/"
//                            + tableInfo.getEntityName() + "PageVO" + StringPool.DOT_JAVA;
//                }
//            });

            // 自定义 PageQuery
            focList.add(new FileOutConfig(queryPageTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
                            + tableInfo.getEntityName() + "PageQuery" + StringPool.DOT_JAVA;
                }
            });
            cfg.setFileOutConfigList(focList);

            // 自定义 ListQuery
            focList.add(new FileOutConfig(queryListTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
                            + tableInfo.getEntityName() + "ListQuery" + StringPool.DOT_JAVA;
                }
            });
            cfg.setFileOutConfigList(focList);

            // 自定义 InfoQuery
            focList.add(new FileOutConfig(queryInfoTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
                            + tableInfo.getEntityName() + "InfoQuery" + StringPool.DOT_JAVA;
                }
            });
            cfg.setFileOutConfigList(focList);

            // 自定义 InsertQuery
            focList.add(new FileOutConfig(queryInsertTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
                            + tableInfo.getEntityName() + "InsertQuery" + StringPool.DOT_JAVA;
                }
            });

            // 自定义 UpdateQuery
            focList.add(new FileOutConfig(queryUpdateTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/query/"
                            + tableInfo.getEntityName() + "UpdateQuery" + StringPool.DOT_JAVA;
                }
            });

            // 自定义 PageVO
            focList.add(new FileOutConfig(voPageTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/vo/"
                            + tableInfo.getEntityName() + "PageVO" + StringPool.DOT_JAVA;
                }
            });

            // 自定义 ListVO
            focList.add(new FileOutConfig(voListTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/vo/"
                            + tableInfo.getEntityName() + "ListVO" + StringPool.DOT_JAVA;
                }
            });

            // 自定义 InfoVO
            focList.add(new FileOutConfig(voInfoTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/src/main/java/" + GeneratorConstant.GENERATOR_PACKAGE_PATH_CUSTOM + "/vo/"
                            + tableInfo.getEntityName() + "InfoVO" + StringPool.DOT_JAVA;
                }
            });
        }

        cfg.setFileOutConfigList(focList);

        return cfg;
    }

    /**
     * 自定义模板配置
     */
    private static TemplateConfig setTemplateConfig() {
        TemplateConfig tc = new TemplateConfig();
        tc.setController(controllerTemplatePath);
//        tc.setEntity(entityTemplatePath);
//        tc.setEntityKt(entityTemplatePath);
        tc.setMapper(mapperTemplatePath);
        tc.setService(serviceTemplatePath);
        tc.setServiceImpl(serviceImplTemplatePath);
        // 清除mapper的xml文件夹， xml文件夹在上面的方法自定义放到resources下的mapper文件夹中
        tc.setXml("");
        return tc;
    }

    /**
     * model 的 注解与驼峰配置, base的类名
     */
    private static StrategyConfig setTable() {
        StrategyConfig sc = new StrategyConfig();
        //是否跳过试图 默认false
        sc.setSkipView(true);
        //是否大写命名 默认false
        sc.setCapitalMode(false);
        // 表映射 驼峰命名
        sc.setNaming(NamingStrategy.underline_to_camel);
        // 字段映射 驼峰
        sc.setColumnNaming(NamingStrategy.underline_to_camel);
        sc.setEntityLombokModel(true);
        sc.setRestControllerStyle(true);
        sc.setEntitySerialVersionUID(false);
        sc.setEntityColumnConstant(false);
        // @TableField 注释开关
        sc.setEntityTableFieldAnnotationEnable(false);
        // 驼峰转连字符 默认false
        sc.setControllerMappingHyphenStyle(true);
        // 去掉表名前缀
        sc.setTablePrefix(GeneratorConstant.REMOVE_TABLE_PREFIX);

        // 设置 base 名字， 默认
//        sc.setSuperMapperClass("");
//        sc.setSuperServiceClass("");
//        sc.setSuperServiceImplClass("");
//        sc.setSuperControllerClass("ASS");

        // 表名，用，隔开  需要生产
        sc.setInclude(tableName.split(","));
        // 不需要生成  二选一
//     sc.setExclude("");
        // 逻辑删除字段名称 (@TableLogic注解是否加在类上)
//        sc.setLogicDeleteFieldName("status");

        return sc;
    }

}
